package org.xx.armory.commons;

import static org.xx.armory.commons.Validators.notNull;
import static org.xx.armory.commons.Validators.validIndex;

/**
 * 带有下标的可排序对象。
 *
 * @param <T>
 *         可排序对象的基础类型。
 */
@Immutable
public final class IndexedComparable<T extends Comparable<T>>
        implements Comparable<IndexedComparable<T>> {
    /**
     * NULL值看做最小。
     */
    public static final int NULLS_MIN = 0;
    /**
     * NULL值看做最大。
     */
    public static final int NULLS_MAX = 1;

    private final int index;
    private final T object;
    private final int nullRule;

    private IndexedComparable(
            int index,
            T object,
            int nullRule
    ) {
        this.index = validIndex(index, "index");
        this.object = object;
        if (nullRule != NULLS_MIN && nullRule != NULLS_MAX) {
            throw new IllegalArgumentException("illegal nullRule \"" + nullRule + "\"");
        }
        this.nullRule = nullRule;
    }

    /**
     * 构造可排序对象。
     *
     * @param index
     *         下标。
     * @param object
     *         基础对象。
     * @param <T>
     *         基础对象的类型。
     * @return 可排序对象。
     */
    public static <T extends Comparable<T>> IndexedComparable<T> of(
            int index,
            T object
    ) {
        return new IndexedComparable<>(index, object, NULLS_MIN);
    }

    /**
     * 构造可排序对象。
     *
     * @param index
     *         下标。
     * @param object
     *         基础对象。
     * @param nullRule
     *         空对象的比较规则。
     * @param <T>
     *         基础对象的类型。
     * @return 可排序对象。
     */
    public static <T extends Comparable<T>> IndexedComparable<T> of(
            int index,
            T object,
            int nullRule
    ) {
        return new IndexedComparable<>(index, object, nullRule);
    }

    /**
     * 获取可排序对象的序号。
     *
     * @return 可排序对象的序号。
     */
    public int getIndex() {
        return this.index;
    }

    /**
     * 获取可排序对象的基础对象。
     *
     * @return 可排序对象的基础对象。
     */
    public T getObject() {
        return this.object;
    }

    @Override
    public int compareTo(
            IndexedComparable<T> other
    ) {
        notNull(other, "other");

        if (this.nullRule != other.nullRule) {
            throw new IllegalArgumentException("conflict null rule");
        }

        if (this.object == null && other.object == null) {
            return 0;
        } else if (this.object != null && other.object == null) {
            return nullRule == NULLS_MIN ? 1 : -1;
        } else if (this.object == null) {
            return nullRule == NULLS_MIN ? -1 : 1;
        } else {
            return this.object.compareTo(other.object);
        }
    }
}
